package com.demo.websocket;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket服务端【将此项目作为服务端，向其他项目发送消息】
 * 访问服务端的url地址
 * id不是表示真的id，而是需要客户端传递过来的参数，【可以使用户ID，姓名，角色，其他等参数 **看自己需要什么参数】
 */
@Component
@ServerEndpoint(value = "/websocket/{id}")
@Slf4j
public class WebSocketServer {

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static ConcurrentHashMap<String, WebSocketServer> webSocketSet = new ConcurrentHashMap<>();
    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;


    private String id = "";

    //连接建立成功调用的方法,存入连接会话中
    @OnOpen
    public void onOpen(@PathParam(value = "id") String id, Session session, EndpointConfig config) {
        this.session = session;
        this.id = id;                     //接收到发送消息的人员编号
        webSocketSet.put(id, this);       //加入set中
        addOnlineCount();                //在线数加1
        log.info("WebSocketServerLog" + "用户编号：" + id + "加入！当前在线人数为" + getOnlineCount());
        log.info("WebSocketServerLog" + "webSocketSet={}", JSON.toJSONString(webSocketSet));
        try {
            Map<String, String> map = new HashMap<>();
            map.put("code", "200");
            map.put("msg", "成功");
            String result = JSONUtil.toJsonStr(map);
            sendMessage(result);
        } catch (IOException e) {
            log.error("websocket IO异常");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this.id);  //从set中删除
        subOnlineCount();             //在线数减1
        log.info("WebSocketServerLog" + this.id + "连接关闭！当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("WebSocketServerLog" + "来自客户端的消息:" + message);
        try {
            Map<String, String> map = new HashMap<>();
            map.put("code", "200");
            map.put("msg", "成功");
            String result = JSONUtil.toJsonStr(map);
            sendtoUser(result, session.getId());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //发生错误调用的方法
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    //只返回消息（默认基础返回）
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 发送信息给指定ID用户，如果用户不在线则返回不在线信息给自己(自己不可以对自己发送信息)
     *
     * @param message
     * @param sendUserId
     * @throws IOException
     */
    public void sendtoUser(String message, String sendUserId) throws IOException {
        //传递过来的所有用户ID
        String[] ids = sendUserId.split(",");

        int conut = 0;
        for (int i = 0; i < ids.length; i++) {
            String userId = ids[i];
            //使用传递过来的ID去webSocket去找这个用户，找到了就给用户发消息
            if (webSocketSet.get(userId) != null) {
                //判断userId是否等于空字符串
                if (!id.equals(userId)) {
                    webSocketSet.get(userId).sendMessage("用户" + id + "发来消息：" + " <br/> " + message);
                } else {
                    ++conut;
                }
                webSocketSet.get(sendUserId).sendMessage(message);
            } else {
                ++conut;
            }
        }
    }

    /**
     * 发送信息给指定ID用户，如果用户不在线则返回不在线信息给自己(自己不可以对自己发送信息)
     *
     * @param object
     * @param sendUserId
     * @throws IOException
     */
    public void sendToUser2(Object object, List<String> sendUserId) throws IOException {
        log.info("符合要求的用户是---" + sendUserId);
        log.info("正处于登录中的用户---" + JSON.toJSONString(webSocketSet));

        //传递过来的所有用户ID
        String jsonObject = JSONUtil.toJsonStr(object);
        log.info("消息内容--：" + jsonObject);

        for (int i = 0; i < sendUserId.size(); i++) {
            String userId = sendUserId.get(i);
            if (webSocketSet.get(userId) != null) {
                log.info("用户--" + userId + "--在websocket中找到，发送消息......");
                webSocketSet.get(userId).sendMessage(jsonObject);
            }
        }
    }

    /**
     * 发送信息给所有人
     *
     * @param message
     * @throws IOException
     */
    public void sendtoAll(String message) throws IOException {
        for (String key : webSocketSet.keySet()) {
            try {
                webSocketSet.get(key).sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 向当前客户端发送对象
     *
     * @param obj 所发送对象
     * @throws IOException
     */
    public void sendObject(Object obj) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        String s = null;
        try {
            s = mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            log.error("转json错误！{}", obj);
        }
        this.sendMessage(s);
    }

    /**
     * 获取当前所有登录的用户ID集合
     *
     * @return
     */
    public List<String> getAllSocketUser() {
        List<String> list = new ArrayList<>();
        for (String key : webSocketSet.keySet()) {
            list.add(key);
        }
        log.info("当前websocket中所有用户集合：--" + list);
        return list;
    }

    //获取在线总人数
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    //在线人数 + 1
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    //在线人数 - 1
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

